from fastapi import APIRouter, HTTPException, Response
from typing import Optional
from src.core.download_manager import DownloadManager
from src.models.download_models import DownloadRequest, BatchDownloadRequest
from src.infrastructure.log_decorators import log_operation
from pydantic import BaseModel, validator, constr
import subprocess
import platform
import os
import asyncio
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from src.core.preview_utils import PreviewUtils
import logging
import psutil  # 需要先安装：pip install psutil
from shutil import rmtree  # 用于删除文件夹
import time
import win32com.client  # 添加在文件顶部其他导入语句附近
from ..core.favorite_path_manager import FavoritePathManager
from sqlalchemy.exc import IntegrityError

router = APIRouter()
download_manager = DownloadManager()
executor = ThreadPoolExecutor(max_workers=4)  # 创建线程池

logger = logging.getLogger(__name__)

# 创建管理器实例
favorite_path_manager = FavoritePathManager()

# 在文件顶部添加内存存储
favorite_paths = []  # 用于存储常用路径

# 添加在文件顶部其他 BaseModel 定义附近
class FavoritePathCreate(BaseModel):
    path: constr(min_length=1, max_length=255)  # 限制路径长度
    name: constr(min_length=1, max_length=100)  # 限制名称长度

    @validator('path')
    def validate_path(cls, v):
        try:
            # 规范化路径
            path = Path(v).resolve()
            
            # 检查路径是否存在
            if not path.exists():
                raise ValueError("Path does not exist")
                
            # 检查是否是目录
            if not path.is_dir():
                raise ValueError("Path must be a directory")
                
            # 检查是否有读取权限
            if not os.access(path, os.R_OK):
                raise ValueError("No permission to access this path")
                
            # 检查路径是否在允许的盘符中
            valid_drives = [
                p.mountpoint 
                for p in psutil.disk_partitions() 
                if p.mountpoint
            ]
            drive_prefix = f"{path.drive}\\" if path.drive else None
            if not drive_prefix or drive_prefix not in valid_drives:
                raise ValueError(f"Path must be in one of these drives: {', '.join(valid_drives)}")
                
            return str(path)
        except ValueError:
            raise
        except Exception as e:
            raise ValueError(f"Invalid path: {str(e)}")

    @validator('name')
    def validate_name(cls, v):
        # 检查名称是否包含非法字符
        invalid_chars = '<>:"/\\|?*'
        if any(char in v for char in invalid_chars):
            raise ValueError(f"Name cannot contain these characters: {invalid_chars}")
        return v.strip()  # 去除首尾空格

# 新增文件系统相关路由
@router.get("/tree")
@log_operation("file-system")
async def get_file_system_tree(path: Optional[str] = None, max_depth: int = 3):
    """获取文件系统树结构"""
    try:
        return await download_manager.get_file_system_tree(path, max_depth)
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

class OpenLocationRequest(BaseModel):
    path: str

def open_file_in_explorer(path: str) -> None:
    """在新线程中打开文件位置"""
    if platform.system() == "Windows":
        subprocess.Popen(f'explorer /select,"{path}"', shell=True)
    elif platform.system() == "Darwin":  # macOS
        subprocess.Popen(['open', '-R', path])
    else:  # Linux
        subprocess.Popen(['xdg-open', os.path.dirname(path)])

@log_operation("file-system")
@router.post("/open-location")
async def open_file_location(request: OpenLocationRequest):
    """打开文件所在位置"""
    try:
        path = request.path
        if not os.path.exists(path):
            raise HTTPException(status_code=404, detail="件不存在")

        # 使用线程池异步执行打开文件操作
        loop = asyncio.get_event_loop()
        await loop.run_in_executor(executor, open_file_in_explorer, path)
        
        return {"success": True}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e)) 

@router.post("/tree/invalidate-cache")
@log_operation("file-system")
async def invalidate_cache(path: Optional[str] = None):
    """清除目录树缓存"""
    try:
        await download_manager.invalidate_file_system_cache(path)
        return {"success": True}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e)) 

def validate_path(path: str) -> Path:
    """验证路径是否在允许的范围内"""
    try:
        abs_path = Path(path).resolve()
        # 获取所有可用的盘符
        valid_drives = [
            p.mountpoint 
            for p in psutil.disk_partitions() 
            if p.mountpoint  # 确保挂载点存在
        ]
        
        # 检查路径是否在允许的盘符中
        drive_prefix = f"{abs_path.drive}\\" if abs_path.drive else None
        if not drive_prefix or drive_prefix not in valid_drives:
            raise HTTPException(
                status_code=403, 
                detail=f"Access denied: Path must be in one of these drives: {', '.join(valid_drives)}"
            )
            
        # 检查路径是否存在
        if not abs_path.exists():
            raise HTTPException(status_code=404, detail="Path not found")
            
        return abs_path
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

@router.get("/list")
async def list_directory(path: str = "H:\\", show_hidden: bool = False):
    """列出目录内容"""
    logger.info(f"Listing directory: {path}, show_hidden: {show_hidden}")
    try:
        # 如果路径为空，默认使用 H 盘
        if not path:
            path = "H:\\"
            
        abs_path = validate_path(path)
        logger.info(f"Resolved path: {abs_path}")
        if not abs_path.exists():
            logger.error(f"Path not found: {abs_path}")
            raise HTTPException(status_code=404, detail="Path not found")
        
        if not abs_path.is_dir():
            logger.error(f"Not a directory: {abs_path}")
            raise HTTPException(status_code=400, detail="Path is not a directory")
            
        items = []
        for entry in os.scandir(abs_path):
            try:
                # 如果不显示隐藏文件，跳过以.开头的文件
                if not show_hidden and entry.name.startswith('.'):
                    continue
                    
                stat = entry.stat()
                item = {
                    "name": entry.name,
                    "file_path": str(entry.path),
                    "file_type": "directory" if entry.is_dir() else PreviewUtils.get_file_type(entry.path),
                    "size": stat.st_size,
                    "modified": stat.st_mtime,
                    "is_dir": entry.is_dir()
                }
                items.append(item)
            except Exception as e:
                logger.error(f"Error processing entry {entry.path}: {str(e)}")
                continue
        
        logger.info(f"Found {len(items)} items")
        return {
            "status": "success",
            "data": items
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error listing directory: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/content")
async def get_file_content(path: str):
    """获取文件内容"""
    try:
        logger.info(f"Getting content for file: {path}")
        file_path = Path(path).resolve()
        
        if not file_path.exists():
            logger.error(f"File not found: {file_path}")
            raise HTTPException(status_code=404, detail="File not found")
            
        if not file_path.is_file():
            logger.error(f"Path is not a file: {file_path}")
            raise HTTPException(status_code=400, detail="Path is not a file")
            
        preview_utils = PreviewUtils()
        file_type = preview_utils.get_file_type(str(file_path))
        logger.info(f"File type detected: {file_type}")
        
        # 图片和 PDF 文件直接返回二进制内容
        if file_type in ['image', 'pdf']:
            with open(file_path, 'rb') as f:
                content = f.read()
                media_type = (
                    f"image/{file_path.suffix[1:]}" 
                    if file_type == 'image' 
                    else 'application/pdf'
                )
                return Response(
                    content=content,
                    media_type=media_type
                )
        
        # 其他文件类型返回预览内容
        preview_data = None
        if file_type == 'text':
            preview_data = preview_utils.create_text_preview(str(file_path))
        elif file_type in ['word', 'powerpoint', 'excel']:  # 添加 excel 到支持的类型中
            preview_data = preview_utils.create_office_preview(str(file_path))
        else:
            preview_data = "此文件类型暂不支持预览"
            
        return {
            "status": "success",
            "data": {
                "file_path": str(file_path),
                "file_type": file_type,
                "preview_data": preview_data,
                "size": file_path.stat().st_size,
                "modified": file_path.stat().st_mtime
            }
        }
    except Exception as e:
        logger.error(f"Error getting file content: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/download")
async def download_file(path: str):
    """下载文件"""
    try:
        file_path = Path(path).resolve()
        if not file_path.exists():
            raise HTTPException(status_code=404, detail="File not found")
            
        if not file_path.is_file():
            raise HTTPException(status_code=400, detail="Path is not a file")
            
        # 复用 download_manager 的下载功能
        return await download_manager.download_file(str(file_path))
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/thumbnail")
async def get_thumbnail(path: str):
    """获取图片缩略图"""
    try:
        file_path = Path(path).resolve()
        if not file_path.exists():
            raise HTTPException(status_code=404, detail="File not found")
            
        if not file_path.is_file():
            raise HTTPException(status_code=400, detail="Path is not a file")
            
        preview_utils = PreviewUtils()
        thumbnail = preview_utils.create_image_preview(str(file_path))
        
        if not thumbnail:
            raise HTTPException(status_code=400, detail="Failed to create thumbnail")
            
        return Response(
            content=thumbnail,
            media_type="image/png"
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/drives")
async def get_drives():
    """获取系统所有盘符信息"""
    try:
        drives = []
        partitions = psutil.disk_partitions()
        
        # 收集所有盘符信息
        for partition in partitions:
            try:
                usage = psutil.disk_usage(partition.mountpoint)
                drive_info = {
                    "path": partition.mountpoint,
                    "label": f"{partition.mountpoint} ({partition.device})",
                    "freeSpace": usage.free,
                    "totalSpace": usage.total,
                    "available": True
                }
                drives.append(drive_info)
            except Exception:
                drives.append({
                    "path": partition.mountpoint,
                    "label": f"{partition.mountpoint} ({partition.device})",
                    "freeSpace": 0,
                    "totalSpace": 0,
                    "available": False
                })
        
        # 将 H 盘移到列表第一位
        drives.sort(key=lambda x: 0 if x['path'].startswith('H:') else 1)
                
        return {
            "status": "success",
            "data": drives
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

class RenameRequest(BaseModel):
    old_path: str
    new_name: str

@router.post("/rename")
async def rename_item(request: RenameRequest):
    """重命名文件或文件夹"""
    try:
        old_path = Path(request.old_path).resolve()
        if not old_path.exists():
            raise HTTPException(status_code=404, detail="Path not found")
            
        new_path = old_path.parent / request.new_name
        if new_path.exists():
            raise HTTPException(status_code=400, detail="File already exists")
            
        old_path.rename(new_path)
        return {"status": "success"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.delete("/delete")
async def delete_item(path: str):  # 使用查询参数
    """删除文件或文件夹"""
    try:
        file_path = Path(path).resolve()
        if not file_path.exists():
            raise HTTPException(status_code=404, detail="Path not found")
            
        if file_path.is_dir():
            rmtree(file_path)  # 递归删除文件夹
        else:
            file_path.unlink()  # 删除文件
            
        return {"status": "success"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# 修改常用路径相关的接口
@router.get("/favorite-paths")
async def get_favorite_paths():
    """获取常用路径列表"""
    try:
        paths = favorite_path_manager.get_all()
        return {
            "status": "success",
            "data": [
                {
                    "id": p.id,
                    "path": p.path,
                    "name": p.name,
                    "createTime": p.create_time.timestamp()
                }
                for p in paths
            ]
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/favorite-paths")
async def add_favorite_path(request: FavoritePathCreate):
    """添加常用路径"""
    try:
        # 验证已经由 Pydantic 模型完成
        try:
            favorite_path = favorite_path_manager.add(request.path, request.name)
            return {
                "status": "success",
                "data": favorite_path.to_dict()
            }
        except IntegrityError:
            raise HTTPException(
                status_code=400, 
                detail="This path is already in favorites"
            )
            
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error adding favorite path: {str(e)}")
        raise HTTPException(
            status_code=500, 
            detail=f"Failed to add favorite path: {str(e)}"
        )

@router.delete("/favorite-paths/{path_id}")
async def delete_favorite_path(path_id: int):
    """删除常用路径"""
    try:
        if favorite_path_manager.delete(path_id):
            return {"status": "success"}
        raise HTTPException(status_code=404, detail="Path not found")
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# 添加一些默认的常用路径
@router.post("/favorite-paths/init")
async def init_favorite_paths():
    """初始化一些默认的常用路径"""
    try:
        if not favorite_paths:  # 只在列表为空时初始化
            default_paths = [
                ("H:\\Download", "下载目录"),
                ("H:\\Documents", "文档"),
                ("H:\\Pictures", "图片"),
                ("H:\\Videos", "视频")
            ]
            
            current_time = time.time()
            for path, name in default_paths:
                if os.path.exists(path):
                    favorite_paths.append({
                        "id": int(current_time * 1000),
                        "path": path,
                        "name": name,
                        "createTime": current_time
                    })
                    current_time += 1
                    
        return {
            "status": "success",
            "data": favorite_paths
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/validate-path")
async def validate_path_exists(path: str):
    """验证路径是否存在"""
    try:
        abs_path = Path(path).resolve()
        if not abs_path.exists():
            raise HTTPException(status_code=404, detail="Path not found")
            
        return {
            "status": "success",
            "data": {
                "path": str(abs_path),
                "exists": True
            }
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

@router.post("/select-folder")
async def select_folder(request: dict):
    """开文件夹选择对话框"""
    try:
        initial_path = request.get('initial_path', '')
        if platform.system() == "Windows":
            import tkinter as tk
            from tkinter import filedialog
            
            root = tk.Tk()
            root.withdraw()  # 隐藏主窗口
            
            path = filedialog.askdirectory(initialdir=initial_path)
            if path:
                return {
                    "status": "success",
                    "data": {
                        "path": path
                    }
                }
            
        raise HTTPException(status_code=400, detail="Operation cancelled or not supported")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))